"""
聊天服务模块

负责处理聊天逻辑和模型调用，是整个对话系统的核心服务。
集成了多模型支持、RAG检索增强、提示词管理等功能。

主要功能：
1. 多轮对话管理
2. 流式和非流式响应
3. 模型调用和结果处理
4. RAG检索增强生成
5. 对话长度控制
6. 会话状态管理

作者: LLM Chat System
版本: 1.0.0
"""

from typing import List, Optional, Dict, Any, AsyncGenerator, Union
from sqlalchemy.orm import Session
import asyncio
import json
from datetime import datetime
from app.core.logging_config import get_logger

# 导入数据模型
from app.models.conversation import Conversation, Message
from app.models.model_config import ModelConfig
from app.models.knowledge_base import KnowledgeBase
from app.models.prompt_template import PromptTemplate

# 导入业务服务
from app.services.conversation_service import ConversationService
from app.services.model_service import ModelService
from app.services.knowledge_base_service import KnowledgeBaseService
from app.services.prompt_service import PromptService
from app.services.user_settings_service import UserSettingsService

# 导入核心管理器
from app.core.llm_manager import LLMManager
from app.core.rag_manager import RAGManager
from app.core.config import settings


class ChatService:
    """
    聊天服务类
    
    整合了所有聊天相关的服务，提供统一的聊天接口。
    负责协调对话管理、模型调用、知识库检索等功能。
    """
    
    def __init__(self):
        """
        初始化聊天服务
        
        创建所有依赖的服务实例：
        - 对话服务：管理对话和消息
        - 模型服务：管理模型配置
        - 知识库服务：管理知识库和文档
        - 提示词服务：管理提示词模板
        - LLM管理器：处理模型调用
        - RAG管理器：处理检索增强生成
        """
        self.conversation_service = ConversationService()      # 对话管理服务
        self.model_service = ModelService()                    # 模型管理服务
        self.knowledge_base_service = KnowledgeBaseService()    # 知识库管理服务
        self.prompt_service = PromptService()                  # 提示词管理服务
        self.llm_manager = LLMManager()                        # LLM调用管理器
        self.rag_manager = RAGManager()                        # RAG检索管理器
        self.logger = get_logger(__name__)                     # 日志记录器
    
    async def generate_conversation_title(
        self,
        db: Session,
        conversation_id: int,
        messages: Optional[List[Dict[str, Any]]] = None,
        model_id: int = None,
        user_id: Optional[int] = None
    ) -> Optional[str]:
        """
        生成对话标题
        
        基于完整的对话历史生成标题，智能选择对话开头和最近的内容。
        
        Args:
            db: 数据库会话
            conversation_id: 对话ID
            messages: 对话消息列表（可选，如果不提供则从数据库获取）
            model_id: 用于生成标题的模型ID
            user_id: 用户ID（用于权限验证）
            
        Returns:
            Optional[str]: 生成的标题，如果生成失败则返回None
        """
        try:
            self.logger.info(f"开始生成对话标题: conversation_id={conversation_id}, model_id={model_id}")
            
            # 如果没有提供消息，从数据库获取完整的对话历史
            if messages is None or len(messages) == 0:
                self.logger.info(f"从数据库获取对话历史: conversation_id={conversation_id}")
                
                # 验证对话是否存在以及用户是否有权限
                if user_id:
                    conversation = self.conversation_service.get_conversation_with_messages(
                        db=db,
                        conversation_id=conversation_id,
                        user_id=user_id
                    )
                    if not conversation:
                        self.logger.error(f"对话不存在或无权限访问: conversation_id={conversation_id}, user_id={user_id}")
                        return None
                
                # 获取完整的消息历史
                db_messages = self.conversation_service.get_conversation_messages(
                    db=db,
                    conversation_id=conversation_id
                )
                
                # 转换为字典格式
                messages = [
                    {
                        "role": msg.role,
                        "content": msg.content
                    }
                    for msg in db_messages
                ]
                
                self.logger.info(f"获取到 {len(messages)} 条历史消息")
            
            if not messages or len(messages) == 0:
                self.logger.warning(f"对话无消息，无法生成标题: conversation_id={conversation_id}")
                return None
            
            # 获取模型配置
            model_config = self.model_service.get(db, model_id)
            if not model_config:
                self.logger.error(f"模型配置不存在: model_id={model_id}")
                return None
            
            # 构建标题生成提示词（基于完整历史）
            title_prompt = self._build_title_generation_prompt(messages)
            
            # 检测是否为推理模型（如 deepseek-reasoner），推理模型需要更多token进行思考
            is_reasoning_model = "reasoner" in model_config.model_name.lower()
            max_tokens = 300 if is_reasoning_model else 50
            
            self.logger.info(f"模型类型: {model_config.model_name}, 是否推理模型: {is_reasoning_model}, max_tokens: {max_tokens}")
            
            # 调用LLM生成标题
            title_response = await self.llm_manager.chat_completion(
                model_config=model_config,
                messages=[{"role": "user", "content": title_prompt}],
                model_settings={
                    "max_tokens": max_tokens,  # 推理模型需要更多token进行推理和输出
                    "temperature": 0.3  # 较低的温度确保标题更准确
                }
            )
            
            if title_response and title_response.get("content"):
                title = title_response["content"].strip()
                # 清理标题，移除可能的引号等
                title = title.strip('"\'')
                # 限制标题长度
                if len(title) > 30:
                    title = title[:30]
                self.logger.info(f"标题生成成功: conversation_id={conversation_id}, title='{title}'")
                return title
            else:
                self.logger.warning(f"标题生成失败: conversation_id={conversation_id}, response={title_response}")
                return None
                
        except Exception as e:
            self.logger.error(f"生成对话标题失败: conversation_id={conversation_id}, error={str(e)}", exc_info=True)
            return None
    
    def _build_title_generation_prompt(self, messages: List[Dict[str, Any]]) -> str:
        """
        构建标题生成提示词
        
        智能选择对话的关键部分：
        1. 如果对话少于6轮，使用全部内容
        2. 如果对话超过6轮，使用：开头3轮 + 最近2轮
        3. 重点关注用户的问题和需求
        
        Args:
            messages: 完整的对话消息列表
            
        Returns:
            str: 标题生成提示词
        """
        total_messages = len(messages)
        self.logger.info(f"构建标题提示词，总消息数: {total_messages}")
        
        # 智能选择消息
        selected_messages = []
        
        if total_messages <= 6:
            # 对话较短，使用全部消息
            selected_messages = messages
            self.logger.info("使用全部消息构建标题")
        else:
            # 对话较长，选择开头3轮 + 最近2轮
            # 开头3轮（前6条消息，3轮对话）
            first_messages = messages[:6]
            # 最近2轮（最后4条消息，2轮对话）
            recent_messages = messages[-4:]
            
            selected_messages = first_messages + [{"role": "system", "content": "..."}] + recent_messages
            self.logger.info(f"使用开头3轮({len(first_messages)}条) + 最近2轮({len(recent_messages)}条)消息")
        
        # 构建对话内容
        conversation_text = ""
        for i, msg in enumerate(selected_messages):
            role = msg.get("role", "")
            content = msg.get("content", "")
            
            # 跳过system消息（除了分隔符）
            if role == "system":
                if content == "...":
                    conversation_text += "\n...(中间省略部分对话)...\n\n"
                continue
            
            # 格式化角色名称
            role_name = "用户" if role == "user" else "助手"
            
            # 限制每条消息的长度
            if len(content) > 200:
                content = content[:200] + "..."
            
            conversation_text += f"{role_name}: {content}\n"
        
        # 限制总长度
        if len(conversation_text) > 800:
            conversation_text = conversation_text[:800] + "..."
            self.logger.warning(f"对话内容过长，截断到800字符")
        
        # 构建提示词
        prompt = f"""请根据以下对话内容，生成一个简洁准确的标题。

对话内容：
{conversation_text}

要求：
1. 标题要概括对话的核心主题
2. 长度不超过15个字
3. 直接输出标题文字，不要包含引号或其他符号

标题："""
        
        self.logger.debug(f"生成的提示词长度: {len(prompt)} 字符")
        
        return prompt

    async def send_message(
        self,
        db: Session,
        conversation_id: int,
        user_id: int,
        message: str,
        model_settings: Optional[Dict[str, Any]] = None,
        knowledge_base_id: Optional[int] = None,
        model_config_id: Optional[int] = None
    ) -> Dict[str, Any]:
        """
        发送消息并获取回复（非流式）
        
        Args:
            db: 数据库会话
            conversation_id: 对话ID
            user_id: 用户ID
            message: 消息内容
            model_settings: 模型设置（可选）
            knowledge_base_id: 知识库ID（可选）
            model_config_id: 模型配置ID（可选，优先级最高）
        
        Returns:
            Dict: 包含回复内容的字典
        """
        
        # 获取对话信息
        conversation = self.conversation_service.get_conversation_with_messages(
            db, conversation_id, user_id
        )
        if not conversation:
            raise ValueError("对话不存在或无权限访问")
        
        # 检查对话长度
        length_check = self.conversation_service.check_conversation_length(conversation)
        if length_check["exceeded"]:
            return {
                "success": False,
                "message": length_check["message"],
                "conversation_exceeded": True
            }
        
        # 添加用户消息
        user_message = self.conversation_service.add_message(
            db, conversation_id, "user", message
        )
        
        # 获取模型配置（优先级：请求参数 > 对话配置 > 默认模型）
        model_config = None
        if model_config_id:
            # 优先使用请求中指定的模型
            self.logger.info(f"使用请求中指定的模型: model_config_id={model_config_id}")
            model_config = self.model_service.get(db, model_config_id)
            if not model_config:
                self.logger.warning(f"请求的模型不存在: model_config_id={model_config_id}")
        
        if not model_config and conversation.model_config_id:
            # 其次使用对话关联的模型
            self.logger.info(f"使用对话关联的模型: model_config_id={conversation.model_config_id}")
            model_config = self.model_service.get(db, conversation.model_config_id)
        
        if not model_config:
            # 最后使用默认模型
            self.logger.info("使用默认模型")
            model_config = self.model_service.get_default_model(db)
        
        if not model_config:
            raise ValueError("未找到可用的模型配置")
        
        self.logger.info(f"最终使用模型: {model_config.name} (ID: {model_config.id}, Provider: {model_config.provider})")
        
        # 获取历史消息
        history_messages = self.conversation_service.get_conversation_messages(
            db, conversation_id, limit=20
        )
        
        # 构建消息历史
        messages = []
        
        # 添加系统提示词
        system_prompt = await self._build_system_prompt(
            db, conversation, model_config, knowledge_base_id
        )
        if system_prompt:
            messages.append({"role": "system", "content": system_prompt})
        
        # 添加历史消息
        for msg in history_messages:
            messages.append({
                "role": msg.role,
                "content": msg.content
            })
        
        try:
            # 记录模型设置
            if model_settings:
                self.logger.info(f"使用前端传递的模型设置: {model_settings}")
            else:
                self.logger.info("使用模型配置的默认设置")
            
            # 调用模型（非流式）
            return await self._get_response(
                db, conversation_id, model_config, messages, model_settings, knowledge_base_id
            )
                
        except Exception as e:
            # 添加错误消息
            self.conversation_service.add_message(
                db, conversation_id, "assistant", 
                f"抱歉，处理您的请求时出现错误：{str(e)}"
            )
            raise e
    
    async def send_message_stream(
        self,
        db: Session,
        conversation_id: int,
        user_id: int,
        message: str,
        model_settings: Optional[Dict[str, Any]] = None,
        knowledge_base_id: Optional[int] = None,
        model_config_id: Optional[int] = None
    ) -> AsyncGenerator[Dict[str, Any], None]:
        """
        发送消息并获取回复（流式）
        
        Args:
            db: 数据库会话
            conversation_id: 对话ID
            user_id: 用户ID
            message: 消息内容
            model_settings: 模型设置（可选）
            knowledge_base_id: 知识库ID（可选）
            model_config_id: 模型配置ID（可选，优先级最高）
        
        Yields:
            Dict: 流式响应数据块
        """
        
        # 获取对话信息
        conversation = self.conversation_service.get_conversation_with_messages(
            db, conversation_id, user_id
        )
        if not conversation:
            raise ValueError("对话不存在或无权限访问")
        
        # 检查对话长度
        length_check = self.conversation_service.check_conversation_length(conversation)
        if length_check["exceeded"]:
            yield {
                "type": "error",
                "error": length_check["message"]
            }
            return
        
        # 添加用户消息
        user_message = self.conversation_service.add_message(
            db, conversation_id, "user", message
        )
        
        # 获取模型配置（优先级：请求参数 > 对话配置 > 默认模型）
        model_config = None
        if model_config_id:
            # 优先使用请求中指定的模型
            self.logger.info(f"流式请求使用指定的模型: model_config_id={model_config_id}")
            model_config = self.model_service.get(db, model_config_id)
            if not model_config:
                self.logger.warning(f"请求的模型不存在: model_config_id={model_config_id}")
        
        if not model_config and conversation.model_config_id:
            # 其次使用对话关联的模型
            self.logger.info(f"流式请求使用对话关联的模型: model_config_id={conversation.model_config_id}")
            model_config = self.model_service.get(db, conversation.model_config_id)
        
        if not model_config:
            # 最后使用默认模型
            self.logger.info("流式请求使用默认模型")
            model_config = self.model_service.get_default_model(db)
        
        if not model_config:
            yield {
                "type": "error",
                "error": "未找到可用的模型配置"
            }
            return
        
        self.logger.info(f"流式请求最终使用模型: {model_config.name} (ID: {model_config.id}, Provider: {model_config.provider})")
        
        # 获取历史消息
        history_messages = self.conversation_service.get_conversation_messages(
            db, conversation_id, limit=20
        )
        
        # 构建消息历史
        messages = []
        
        # 添加系统提示词
        system_prompt = await self._build_system_prompt(
            db, conversation, model_config, knowledge_base_id
        )
        if system_prompt:
            messages.append({"role": "system", "content": system_prompt})
        
        # 添加历史消息
        for msg in history_messages:
            messages.append({
                "role": msg.role,
                "content": msg.content
            })
        
        try:
            # 记录模型设置
            if model_settings:
                self.logger.info(f"流式响应使用前端传递的模型设置: {model_settings}")
            else:
                self.logger.info("流式响应使用模型配置的默认设置")
            
            # 直接调用流式响应
            async for chunk in self._stream_response(
                db, conversation_id, model_config, messages, model_settings, knowledge_base_id
            ):
                yield chunk
                
        except Exception as e:
            yield {
                "type": "error",
                "error": str(e)
            }
    
    async def _build_system_prompt(
        self,
        db: Session,
        conversation: Conversation,
        model_config: ModelConfig,
        knowledge_base_id: Optional[int] = None
    ) -> Optional[str]:
        """
        构建系统提示词
        
        只使用用户明确选择的提示词模板，不添加任何系统默认提示词。
        如果用户未选择提示词模板，则返回 None，不使用任何系统提示。
        """
        
        # 只有用户选择了提示词模板时，才使用提示词
        if conversation.prompt_template_id:
            self.logger.info(f"对话 {conversation.id} 使用用户选择的提示词模板 ID: {conversation.prompt_template_id}")
            template = self.prompt_service.get(db, conversation.prompt_template_id)
            if template:
                self.logger.info(f"找到提示词模板: {template.name}, 内容长度: {len(template.content)}")
                # 增加使用次数
                self.prompt_service.increment_usage_count(db, template.id)
                
                # 直接返回用户选择的提示词内容
                self.logger.info(f"使用用户提示词模板 '{template.name}' (对话 {conversation.id})")
                return template.content
            else:
                self.logger.warning(f"提示词模板 ID {conversation.prompt_template_id} 不存在，不使用提示词")
                return None
        else:
            self.logger.info(f"对话 {conversation.id} 未选择提示词模板，不使用系统提示词")
            return None
    
    async def _get_response(
        self,
        db: Session,
        conversation_id: int,
        model_config: ModelConfig,
        messages: List[Dict[str, str]],
        model_settings: Optional[Dict[str, Any]] = None,
        knowledge_base_id: Optional[int] = None
    ) -> Dict[str, Any]:
        """获取非流式响应"""
        
        # 如果指定了知识库，进行RAG检索
        if knowledge_base_id:
            try:
                self.logger.info(f"开始RAG检索: knowledge_base_id={knowledge_base_id}")
                knowledge_base = self.knowledge_base_service.get(db, knowledge_base_id)
                if knowledge_base:
                    # 获取用户最后一条消息进行检索
                    user_message = messages[-1]["content"] if messages and messages[-1]["role"] == "user" else ""
                    if user_message:
                        # 检索相关文档
                        relevant_docs = await self.rag_manager.retrieve_documents(
                            knowledge_base, user_message, top_k=5
                        )
                        
                        if relevant_docs:
                            self.logger.info(f"检索到 {len(relevant_docs)} 个相关文档")
                            # 构建增强的上下文
                            context = "\n\n".join([doc.page_content for doc in relevant_docs])
                            enhanced_message = f"""
基于以下上下文信息回答问题：

上下文：
{context}

问题：{user_message}

请根据上下文信息提供准确的回答，如果上下文中没有相关信息，请说明。
"""
                            # 替换最后一条用户消息
                            messages[-1]["content"] = enhanced_message
                            self.logger.info(f"RAG增强后的消息长度: {len(enhanced_message)}")
                        else:
                            self.logger.warning(f"知识库 {knowledge_base.name} 中未找到相关文档")
                else:
                    self.logger.warning(f"知识库 ID {knowledge_base_id} 不存在")
            except Exception as e:
                self.logger.error(f"RAG检索失败: {str(e)}")
        
        # 调用模型
        response = await self.llm_manager.chat_completion(
            model_config, messages, model_settings
        )
        
        # 添加助手回复
        assistant_message = self.conversation_service.add_message(
            db, conversation_id, "assistant", response["content"],
            metadata=response.get("metadata"),
            token_count=response.get("token_count")
        )
        
        return {
            "success": True,
            "message": response["content"],
            "message_id": assistant_message.id,
            "token_count": response.get("token_count"),
            "metadata": response.get("metadata")
        }
    
    async def _stream_response(
        self,
        db: Session,
        conversation_id: int,
        model_config: ModelConfig,
        messages: List[Dict[str, str]],
        model_settings: Optional[Dict[str, Any]] = None,
        knowledge_base_id: Optional[int] = None
    ) -> AsyncGenerator[Dict[str, Any], None]:
        """获取流式响应"""
        
        # 如果指定了知识库，进行RAG检索
        if knowledge_base_id:
            try:
                self.logger.info(f"开始RAG检索（流式）: knowledge_base_id={knowledge_base_id}")
                knowledge_base = self.knowledge_base_service.get(db, knowledge_base_id)
                if knowledge_base:
                    # 获取用户最后一条消息进行检索
                    user_message = messages[-1]["content"] if messages and messages[-1]["role"] == "user" else ""
                    if user_message:
                        # 检索相关文档
                        relevant_docs = await self.rag_manager.retrieve_documents(
                            knowledge_base, user_message, top_k=5
                        )
                        
                        if relevant_docs:
                            self.logger.info(f"检索到 {len(relevant_docs)} 个相关文档（流式）")
                            # 构建增强的上下文
                            context = "\n\n".join([doc.page_content for doc in relevant_docs])
                            enhanced_message = f"""
基于以下上下文信息回答问题：

上下文：
{context}

问题：{user_message}

请根据上下文信息提供准确的回答，如果上下文中没有相关信息，请说明。
"""
                            # 替换最后一条用户消息
                            messages[-1]["content"] = enhanced_message
                            self.logger.info(f"RAG增强后的消息长度（流式）: {len(enhanced_message)}")
                        else:
                            self.logger.warning(f"知识库 {knowledge_base.name} 中未找到相关文档（流式）")
                else:
                    self.logger.warning(f"知识库 ID {knowledge_base_id} 不存在（流式）")
            except Exception as e:
                self.logger.error(f"RAG检索失败（流式）: {str(e)}")
        
        full_content = ""
        token_count = 0
        
        try:
            async for chunk in self.llm_manager.chat_completion_stream(
                model_config, messages, model_settings
            ):
                if chunk.get("content"):
                    full_content += chunk["content"]
                    token_count += chunk.get("token_count", 0)
                    
                    yield {
                        "type": "content",
                        "content": chunk["content"],
                        "token_count": chunk.get("token_count", 0)
                    }
            
            # 流式响应完成后，保存完整消息
            assistant_message = self.conversation_service.add_message(
                db, conversation_id, "assistant", full_content,
                metadata={"stream": True},
                token_count=token_count
            )
            
            yield {
                "type": "done",
                "message_id": assistant_message.id,
                "total_tokens": token_count
            }
            
        except Exception as e:
            yield {
                "type": "error",
                "error": str(e)
            }
    
    async def chat_with_rag(
        self,
        db: Session,
        conversation_id: int,
        user_id: int,
        message: str,
        knowledge_base_id: int,
        stream: bool = False
    ) -> Dict[str, Any]:
        """使用RAG进行聊天"""
        
        # 获取知识库
        knowledge_base = self.knowledge_base_service.get(db, knowledge_base_id)
        if not knowledge_base:
            raise ValueError("知识库不存在")
        
        # 检索相关文档
        relevant_docs = await self.rag_manager.retrieve_documents(
            knowledge_base, message, top_k=5
        )
        
        # 构建增强的提示词
        context = "\n\n".join([doc.page_content for doc in relevant_docs])
        enhanced_message = f"""
基于以下上下文信息回答问题：

上下文：
{context}

问题：{message}

请根据上下文信息提供准确的回答，如果上下文中没有相关信息，请说明。
"""
        
        # 调用普通聊天服务
        return await self.send_message(
            db, conversation_id, user_id, enhanced_message, stream
        )
    
    def get_conversation_summary(
        self,
        db: Session,
        conversation_id: int,
        user_id: int
    ) -> Dict[str, Any]:
        """获取对话摘要"""
        
        conversation = self.conversation_service.get_conversation_with_messages(
            db, conversation_id, user_id
        )
        if not conversation:
            raise ValueError("对话不存在或无权限访问")
        
        messages = self.conversation_service.get_conversation_messages(
            db, conversation_id
        )
        
        return {
            "conversation_id": conversation.id,
            "title": conversation.title,
            "message_count": conversation.message_count,
            "created_at": conversation.created_at,
            "updated_at": conversation.updated_at,
            "model_config": conversation.model_config.name if conversation.model_config else None,
            "knowledge_base": conversation.knowledge_base.name if conversation.knowledge_base else None,
            "prompt_template": conversation.prompt_template.name if conversation.prompt_template else None,
            "recent_messages": [
                {
                    "role": msg.role,
                    "content": msg.content[:100] + "..." if len(msg.content) > 100 else msg.content,
                    "created_at": msg.created_at
                }
                for msg in messages[-5:]  # 最近5条消息
            ]
        }
    